package everything.search.trees;

//MAX PLY = 3

import java.io.File;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.TreeMap;

import everything.Agent;
import everything.Flag;
import everything.WorldMap;
import everything.search.AgentState;
import everything.search.trees.nodes.GameTreeNode;

public abstract class GameTreeSearch {

	protected WorldMap _map;
	protected int _id;
	protected int _idOp;
	protected boolean _memorize;
	
	private TreeMap<AgentState, GameTreeNode> _states;


	private static PrintWriter logger;
	static{
		File f = new File("treelog.txt");
		f.delete();
		try {
			f.createNewFile();
			logger = new PrintWriter(f);
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	protected static void log(String line){
		logger.println(line);
		logger.flush();
	}

	public GameTreeSearch(WorldMap map, int id, int idOp){
		this._map = map;
		this._id = id;
		this._idOp = idOp;
		this._states = new TreeMap<AgentState, GameTreeNode>();
	}
	
	protected abstract GameTreeNode searchMax(GameTreeNode node, int ply);
	protected abstract GameTreeNode searchMin(GameTreeNode node, int ply);

	protected abstract GameTreeNode searchWithParam(AgentState state, int ply);
	
	public GameTreeNode search(AgentState state, int ply, boolean memorize){
		_memorize = memorize;
		log("STARTING SEARCH FOR " + _id + " ("+ply+") WITH " + state);
		GameTreeNode searchNode = searchWithParam(state, ply);
		GameTreeNode tmp = searchNode;
		while (tmp.getParent()!=null){
			System.out.println("BACKTRACK: " + tmp);
			tmp = tmp.getParent();
		}
		System.out.println("BACKTRACK: " + tmp);
		return searchNode;
	}
	
	protected GameTreeNode checkState(AgentState state){
		if (_memorize){
			return _states.get(state);
		}
		return null;
	}
	protected void storeState(AgentState state, GameTreeNode node){
		if (_memorize){
			_states.put(state, node);
		}
	}
	
	protected AgentState getNextState(AgentState state, String direction, boolean escapedIce){

		int bullets = state.getBullets();
		boolean opHealth = state.getHOpponent();

		if ("Fire".equalsIgnoreCase(direction)){
			if (bullets>0){
				bullets--;
				opHealth = opHealth && !Agent.isKilling(state.getX(), state.getY(), bullets!=0, !state.getHealth(), state.getXOpponent(), state.getYOpponent(), _map);
			}
		}

		int selectedX = state.getX();
		int selectedY = state.getY();

		if (state.getOrientation()!=null && ("Fire".equalsIgnoreCase(direction) || !escapedIce)){	//sliding on ice
			direction = state.getOrientation();
		}
		if ("North".equalsIgnoreCase(direction) && _map.brickStatus(selectedX-1, selectedY)!=WorldMap.WALL && (state.getXOpponent()!=selectedX-1 || state.getYOpponent()!=selectedY)){
			selectedX--;
		} else if ("South".equalsIgnoreCase(direction) && _map.brickStatus(selectedX+1, selectedY)!=WorldMap.WALL && (state.getXOpponent()!=selectedX+1 || state.getYOpponent()!=selectedY)){
			selectedX++;
		} else if ("East".equalsIgnoreCase(direction) && _map.brickStatus(selectedX, selectedY+1)!=WorldMap.WALL && (state.getXOpponent()!=selectedX || state.getYOpponent()!=selectedY+1)){
			selectedY++;
		} else if ("West".equalsIgnoreCase(direction) && _map.brickStatus(selectedX, selectedY-1)!=WorldMap.WALL && (state.getXOpponent()!=selectedX || state.getYOpponent()!=selectedY-1)){
			selectedY--;
		}
		int cellStat = _map.brickStatus(selectedX, selectedY); // updated X,Y

		Flag[] flags = new Flag[state.getFlags().length];
		for (int i =0; i< flags.length; i++){
			Flag f = state.getFlags()[i];
			flags[i] = new Flag(f.getX(), f.getY(), f.getValue());
			flags[i].setOwner(f.getOwner());
		}

		int remainingMoves = state.getRemainingMoves() - 1;

		if (cellStat == WorldMap.EMPTY){
			return new AgentState(selectedX, selectedY, null, bullets, state.getHealth(), state.getXOpponent(), state.getYOpponent(), state.getDOpponent(), state.getBOpponent(), opHealth, flags, remainingMoves);
		} else if (cellStat == WorldMap.FLAG) {
			for (int i = 0; i< flags.length; i++){
				Flag tmpFlag = flags[i];
				if (state.getHealth() && tmpFlag.isLocatedIn (selectedX, selectedY) && flags[i].getOwner()==0){
					flags[i].setOwner(_id);
				}
			}
			return new AgentState(selectedX, selectedY, null, bullets, state.getHealth(), state.getXOpponent(), state.getYOpponent(), state.getDOpponent(), state.getBOpponent(), opHealth, flags, remainingMoves);
		} else if (cellStat == WorldMap.ICE){
			if (direction.equalsIgnoreCase("North")){
				if(_map.brickStatus(selectedX - 1, selectedY) == WorldMap.WALL){
					return new AgentState(selectedX, selectedY, null, bullets, state.getHealth(), state.getXOpponent(), state.getYOpponent(), state.getDOpponent(), state.getBOpponent(), opHealth, flags, remainingMoves);
				}
			} else if (direction.equalsIgnoreCase("East")){
				if(_map.brickStatus(selectedX, selectedY + 1) == WorldMap.WALL){
					return new AgentState(selectedX, selectedY, null, bullets, state.getHealth(), state.getXOpponent(), state.getYOpponent(), state.getDOpponent(), state.getBOpponent(), opHealth, flags, remainingMoves);
				}
			} else if (direction.equalsIgnoreCase("South")){
				if(_map.brickStatus(selectedX + 1, selectedY) == WorldMap.WALL){
					return new AgentState(selectedX, selectedY, null, bullets, state.getHealth(), state.getXOpponent(), state.getYOpponent(), state.getDOpponent(), state.getBOpponent(), opHealth, flags, remainingMoves);
				}
			} else if (direction.equalsIgnoreCase("West")){
				if(_map.brickStatus(selectedX, selectedY - 1) == WorldMap.WALL){
					return new AgentState(selectedX, selectedY, null, bullets, state.getHealth(), state.getXOpponent(), state.getYOpponent(), state.getDOpponent(), state.getBOpponent(), opHealth, flags, remainingMoves);
				}
			} else {
				return new AgentState(selectedX, selectedY, null, bullets, state.getHealth(), state.getXOpponent(), state.getYOpponent(), state.getDOpponent(), state.getBOpponent(), opHealth, flags, remainingMoves);
			}
			return new AgentState(selectedX, selectedY, direction, bullets, state.getHealth(), state.getXOpponent(), state.getYOpponent(), state.getDOpponent(), state.getBOpponent(), opHealth, flags, remainingMoves);
		} else {
			System.out.println("###################################");
			System.out.println("cellstat = " + cellStat);
			System.out.println("###################################");
			return null;
		}
	}

	protected AgentState getNextOpState(AgentState state, String direction, boolean escapedIce){

		int bOpponent= state.getBOpponent();
		boolean health= state.getHealth();

		if ("Fire".equalsIgnoreCase(direction)){
			if (bOpponent>0){
				bOpponent--;
				health = health && !Agent.isKilling(state.getXOpponent(), state.getYOpponent(), bOpponent!=0, !state.getHOpponent(), state.getX(), state.getY(), _map);
			}
		}

		int selectedX = state.getXOpponent();
		int selectedY = state.getYOpponent();

		if (state.getDOpponent()!=null && ("Fire".equalsIgnoreCase(direction) || !escapedIce)){	//sliding on ice
			direction = state.getDOpponent();
		}
		if ("North".equalsIgnoreCase(direction) && _map.brickStatus(selectedX-1, selectedY)!=WorldMap.WALL && (state.getX()!=selectedX-1 || state.getY()!=selectedY)){
			selectedX--;
		} else if ("South".equalsIgnoreCase(direction) && _map.brickStatus(selectedX+1, selectedY)!=WorldMap.WALL && (state.getX()!=selectedX+1 || state.getY()!=selectedY)){
			selectedX++;
		} else if ("East".equalsIgnoreCase(direction) && _map.brickStatus(selectedX, selectedY+1)!=WorldMap.WALL && (state.getX()!=selectedX || state.getY()!=selectedY+1)){
			selectedY++;
		} else if ("West".equalsIgnoreCase(direction) && _map.brickStatus(selectedX, selectedY-1)!=WorldMap.WALL && (state.getX()!=selectedX || state.getY()!=selectedY-1)){
			selectedY--;
		}
		int cellStat = _map.brickStatus(selectedX, selectedY); // updated X,Y

		Flag[] flags = new Flag[state.getFlags().length];
		for (int i =0; i< flags.length; i++){
			Flag f = state.getFlags()[i];
			flags[i] = new Flag(f.getX(), f.getY(), f.getValue());
			flags[i].setOwner(f.getOwner());
		}

		int remainingMoves = state.getRemainingMoves()-1;

		if (cellStat == WorldMap.EMPTY){
			return new AgentState(state.getX(), state.getY(), state.getOrientation(), state.getBullets(), health, selectedX, selectedY, null, bOpponent, state.getHOpponent(), flags, remainingMoves);
		} else if (cellStat == WorldMap.FLAG) {
			for (int i = 0; i< flags.length; i++){
				Flag tmpFlag = flags[i];
				if (state.getHOpponent() && tmpFlag.isLocatedIn(selectedX, selectedY) && flags[i].getOwner() == 0){
					flags[i].setOwner(_idOp);
				}
			}
			return new AgentState(state.getX(), state.getY(), state.getOrientation(), state.getBullets(), health, selectedX, selectedY, null, bOpponent, state.getHOpponent(), flags, remainingMoves);
		} else if (cellStat == WorldMap.ICE){
			if (direction.equalsIgnoreCase("North")){
				if(_map.brickStatus(selectedX - 1, selectedY) == WorldMap.WALL){
					return new AgentState(state.getX(), state.getY(), state.getOrientation(), state.getBullets(), health, selectedX, selectedY, null, bOpponent, state.getHOpponent(), flags, remainingMoves);
				}
			} else if (direction.equalsIgnoreCase("East")){
				if(_map.brickStatus(selectedX, selectedY + 1) == WorldMap.WALL){
					return new AgentState(state.getX(), state.getY(), state.getOrientation(), state.getBullets(), health, selectedX, selectedY, null, bOpponent, state.getHOpponent(), flags, remainingMoves);
				}
			} else if (direction.equalsIgnoreCase("South")){
				if(_map.brickStatus(selectedX + 1, selectedY) == WorldMap.WALL){
					return new AgentState(state.getX(), state.getY(), state.getOrientation(), state.getBullets(), health, selectedX, selectedY, null, bOpponent, state.getHOpponent(), flags, remainingMoves);
				}
			} else if (direction.equalsIgnoreCase("West")){
				if(_map.brickStatus(selectedX, selectedY - 1) == WorldMap.WALL){
					return new AgentState(state.getX(), state.getY(), state.getOrientation(), state.getBullets(), health, selectedX, selectedY, null, bOpponent, state.getHOpponent(), flags, remainingMoves);
				}
			} else {
				return new AgentState(state.getX(), state.getY(), state.getOrientation(), state.getBullets(), health, selectedX, selectedY, null, bOpponent, state.getHOpponent(), flags, remainingMoves);
			}
			return new AgentState(state.getX(), state.getY(), state.getOrientation(), state.getBullets(), health, selectedX, selectedY, direction, bOpponent, state.getHOpponent(), flags, remainingMoves);
		} else {
			System.out.println("###################################");
			System.out.println("cellstat = " + cellStat);
			System.out.println("###################################");
			return null;
		}
	}

	/**
	 * returns minimax value of state
	 * a = best score for MAX along the path to state
	 * b = best score for MIN along the path to state
	 * game = game description
	 */
	//	alpha beta pruning
	/*
	public static int maxValue(AgentState state, Game game, ?? a, ?? b){
		if (cutOffTest(state))
			return eval(state);
		for each s in succerssors(state){
			a = max(a, minValue(s,game,a,b));
			if (a >= b)
				return b;
		}
		return a;
	}

	public int minValue (AgentState state, Game game, ?? a, ?? b){
		if (cutOffTest(state))
			return eval(state);
		for each s in succerssors(state){
			b = min(b,maxValue(s,game,a,b))
			if (a >= b)
				return a;
		}
		return b;
	}

	function alphabeta(node, depth, a, b)
    (* b represents previous player best choice - doesn't want it if a would worsen it *)
    if node is a terminal node or depth = 0
        return the heuristic value of node
    foreach child of node
        a := max(a, -alphabeta(child, depth-1, -b, -a))
        (* use symmetry, -b becomes subsequently pruned a *)
        if b<=a
            break                             (* Beta cut-off *)
    return a

(* Initial call *)
alphabeta(origin, depth, -inf, +inf)

	 */
}
